import math, pathlib, sys, os

def hex_to_rgb(hex_color):
    hex_color = hex_color.lstrip('#')
    return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))

def rgb_to_hex(rgb_color):
    return '#{:02x}{:02x}{:02x}'.format(*rgb_color)

def convert_size(size_bytes: int):
    size_name=("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
    if size_bytes == 0:
        return f"{size_bytes} {size_name[0]}"
    i = int(math.floor(math.log(size_bytes, 1024)))
    p = math.pow(1024, i)
    s = round(size_bytes / p, 2)
    return f"{s} {size_name[i]}"

def strtobool(val):
    """Convert a string representation of truth to true (1) or false (0).
    True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
    are 'n', 'no', 'f', 'false', 'off', and '0'.  Raises ValueError if
    'val' is anything else.
    """
    val = val.lower()
    if val in ('y', 'yes', 't', 'true', 'on', '1'):
        return True
    elif val in ('n', 'no', 'f', 'false', 'off', '0'):
        return False
    else:
        raise ValueError("invalid truth value %r" % (val,))

def get_app_root():
    if getattr(sys, 'frozen', False):
        # If the application is frozen (e.g., PyInstaller)
        app_home = pathlib.Path(sys.executable).parent.resolve()
    else:
        app_home = os.path.realpath(sys.path[0])

    return app_home

def correct_path(path: os.PathLike):
    path = str(path)
    new_path = path.replace('\\', '/')
    for char in '<>:"|?*':
        new_path = new_path.replace(char, '')
    new_path_parts: list[str] = new_path.split('/')
    new_path = ''
    for part in new_path_parts:
        part = part.strip()
        part = part.strip('.')
        new_path += (part + '/')
    new_path = new_path.strip('/')
    new_path = pathlib.Path(new_path)
    new_path = new_path.expanduser()
    new_path = new_path.absolute()
    new_path = new_path.resolve()
    final_path = str()
    for part in new_path.parts:
        final_path = os.path.join(final_path, part)
    return final_path

# https://stackoverflow.com/a/65182695
def remove_empty_folders(root):
    folders = sorted(list(os.walk(root))[1:],reverse=True)
    for folder in folders:
        try:
            os.rmdir(folder[0])
        except OSError as error: 
            print("Directory '{}' can not be removed".format(folder[0]))

def pad_or_truncate(some_list, target_len):
    return some_list[:target_len] + [None]*(target_len - len(some_list)) # The hell am I looking at?

def fix_index(lst, index):
    """
    Check if the given index is a valid index for the given list.
    If not, adjust it to the closest valid index within the range of the list.

    Parameters:
    - lst: The list to check the index against.
    - index: The index to check and potentially fix.

    Returns:
    - If the index is valid, returns the original index.
    - If the index is not valid, returns the closest valid index.
    """
    if not isinstance(index, int):
        raise ValueError("Index must be an integer")

    # If the list is empty, any index is invalid
    if not lst:
        raise ValueError("List is empty")

    # Calculate the valid index range
    valid_range = range(len(lst))

    # Check if the index is within the valid range
    if index in valid_range:
        return index
    else:
        # Fix the index by adjusting it to the closest valid index
        fixed_index = max(min(index, valid_range[-1]), valid_range[0])
        return fixed_index
